﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Object = UnityEngine.Object;

public enum ABDataStatus
{
    wwwloading,
    wwwloaded,
    abloaded,
}

#region 加载完成回调
public delegate void OnLoadAsset(OnLoadArg onLoadArg);

public class OnLoadArg
{
    public string key;
    public Object asset;
    public bool loadOk = false;

    public OnLoadArg() { }

    public OnLoadArg(string key, Object asset, bool loadOk)
    {
        this.key = key;
        this.asset = asset;
        this.loadOk = loadOk; 
    }
    
    public void Clear()
    {
        key = null;
        asset = null;
        loadOk = false;
    }
}
#endregion

#region 资源包加载数据
public class ABData
{
    public WWW www;
    public AssetBundle ab;
    public RequestInfo requestInfo;

    /// <summary>
    /// 引用计数
    /// </summary>
    public int refNum;
    /// <summary>
    /// 前置路径
    /// </summary>
    public string prePath;
    /// <summary>
    /// 需要等待数量  针对主资源
    /// </summary>
    public int waitCount;

    ABDataStatus status = ABDataStatus.wwwloading;
    public ABDataStatus Status
    {
        set
        {
            if (status != value)
            {
                status = value;
                if (status == ABDataStatus.abloaded)
                {
                    ab = www.assetBundle;
                    www.Dispose();
                    www = null;
                }
            }
        }
        get { return status; }
    }

    public bool PreLoaded
    {
        get
        {
            waitCount = 0; string depend = "";
            if (ZMgr.ResourceMgr.IsABLoaded(requestInfo.resPath, ref depend))
            {
                waitCount++;
            }
            return waitCount == 0;
        }
    }
    
    public void Destroy()
    {
        if (ab != null)
        {
            ab.Unload(false);
        }
        www = null;
        requestInfo = null;
    }


    public void AddPreLoadedEvent()
    {
        waitCount = 0; string depend = "";
        if (ZMgr.ResourceMgr.IsABLoaded(requestInfo.resPath, ref depend))
        {
            if (string.IsNullOrEmpty(depend))
            {
                ZEventCenter.AddEventListener(depend, OnPreABLoaded);
                waitCount++;
            }
        }
    }

    private void OnPreABLoaded(ZEvent ev)
    {
        ZEventCenter.RemoveEventListener(ev.eventName, OnPreABLoaded);

        waitCount--;
        if (waitCount < 1)
        {
            ZMgr.ResourceMgr.ABLoad(this);
        }
    }
}
#endregion

/// <summary>
/// 资源管理
/// </summary>
public class ResourceMgr : IBaseData
{
    private List<string> publicAssetList;
    //主加载资源
    private Dictionary<string, Object> assetDic;
    //依赖资源缓存 周期为主资源加载完成
    private Dictionary<string, ABData> dependCacheDic;
    //加载完成回调
    private Dictionary<string, OnLoadAsset> onloadDic;
    //后缀
    private Dictionary<Type, string> suffixDic;
    //加载队列
    private ZLoaderQueen queen;

    public bool IsLoaded { get; private set; }

    public bool IsLoading
    {
        get { return queen.QueenModel.isQueening; }
    }

    public float LoadProgress
    {
        get { return IsLoaded ? 1 : queen.QueenModel.Progress; }
    }

    public ResourceMgr()
    {
        publicAssetList = new List<string>();
        suffixDic = new Dictionary<Type, string>();
        assetDic = new Dictionary<string, Object>();
        dependCacheDic = new Dictionary<string, ABData>();
        onloadDic = new Dictionary<string, OnLoadAsset>();
        queen = new ZLoaderQueen("资源加载", 8); Init();
        
        queen.QueenModel.AddEvent(ConstData.COMPLETE, OnComplete);
        queen.QueenModel.AddEvent(ConstData.CHILD_COMPLETE, OnChildComplete);
    }

    public void Init()
    {
        for (int i = 0; i < TableDataMgr.GuideVOMgr.mDataCount; i++)
        {
            var item = TableDataMgr.PublicAtlasMgr.GetItemByIdx(i);
            publicAssetList.Add(item.PublicAtlas.Replace('/', '_') + ".uab");
            string resPath = publicAssetList[publicAssetList.Count - 1];

            string[] depends;
            if (ConfigAssetMgr.GetSingleton().GetDepends(resPath, out depends))
            {
                for (int j = 0; j < depends.Length; j++)
                {
                    publicAssetList.Add(depends[j]);
                }
            }
        }

        suffixDic.Add(typeof(UIAtlas), ".prefab.uab");
        suffixDic.Add(typeof(Texture), ".png.uab");
        suffixDic.Add(typeof(AudioClip), ".mp3.uab");
    }

    private void OnChildComplete(object sender, EventArg eventArg)
    {
        string resPath = queen.CurLoader.Request.resPath;
        if (!dependCacheDic.ContainsKey(resPath))
        {
            EDebug.Log(string.Format("加载完成异常{0}找不到ABData", resPath));
            return;
        }
        ABData abData = dependCacheDic[resPath];
        abData.www = queen.CurLoader.Www;
        abData.requestInfo = queen.CurLoader.Request;
        abData.Status = ABDataStatus.wwwloaded;

        if (abData.PreLoaded)
        {
            ABLoad(abData);
        }
        else
        {
            abData.AddPreLoadedEvent();
        }
    }

    public void ABLoad(ABData abData)
    {
        if (abData == null || abData.requestInfo == null)
        {
            EDebug.Log("abData出现问题，此处为容错处理，后面的逻辑将会丢掉。");
            return;
        }
        string key = abData.requestInfo.key;
        string resPath = abData.requestInfo.resPath;
        abData.Status = ABDataStatus.abloaded;
        if (assetDic.ContainsKey(resPath))
        {
            assetDic[resPath] = abData.ab.mainAsset;
        }
        else
        {
            assetDic.Add(resPath, abData.ab.mainAsset);
        }
        if (key != ConstData.DEPEND_KEY)
        {
            OnLoadAsset onLoad;
            if (onloadDic.TryGetValue(resPath, out onLoad) && onLoad != null)
            {
                onLoad.Invoke(new OnLoadArg(key, assetDic[resPath], true));
                onloadDic.Remove(resPath);
            }
            FreeDepend(resPath);
        }
        ZEventCenter.DispatchEvent(new ZEvent(resPath));
    }

    /// <summary>
    /// 是否依赖资源
    /// </summary>
    /// <param name="resPath"></param>
    private void FreeDepend(string resPath)
    {
        if (publicAssetList.Contains(resPath)) { return; }

        string[] depends; ABData abData;
        if (ConfigAssetMgr.GetSingleton().GetDepends(resPath, out depends))
        {
            for (int i = 0; i < depends.Length; i++)
            {
                if (!publicAssetList.Contains(depends[i]))
                {
                    if (dependCacheDic.TryGetValue(depends[i], out abData))
                    {
                        abData.refNum--;
                        if (abData.refNum < 1)
                        {
                            abData.Destroy();
                            dependCacheDic.Remove(depends[i]);
                        }
                    }
                }
            }
        }

        dependCacheDic[resPath].refNum--;
        if (dependCacheDic[resPath].refNum < 1)
        {
            dependCacheDic[resPath].ab.Unload(false);
            dependCacheDic[resPath].Destroy();
            dependCacheDic.Remove(resPath);
        }
    }
    
    private void OnComplete(object sender, EventArg eventArg)
    {
        IsLoaded = true; queen.Clear();
        UpdateMgr.GetSingleton().RemoveUpdate(queen);
        EventCenter.BroadEvent(eventArg);
    }

    public void Clear()
    {
        IsLoaded = false; BehaviourUtil.StopAllCoroutines();
        for (int i = assetDic.Count - 1; i >= 0; i--)
        {
            var item = assetDic.ElementAt(i);
            if (!publicAssetList.Contains(item.Key))
            {
                assetDic.Remove(item.Key);
            }
        }
        queen.Clear(); onloadDic.Clear();
        for (int i = dependCacheDic.Count - 1; i >= 0; i--)
        {
            var item = dependCacheDic.ElementAt(i);
            if (!publicAssetList.Contains(item.Key))
            {
                item.Value.Destroy();
                dependCacheDic.Remove(item.Key);
            }
        }
        UpdateMgr.GetSingleton().RemoveUpdate(queen);
        Resources.UnloadUnusedAssets();
    }

    /// <summary>
    /// 界面资源预加载
    /// </summary>
    public void LoadResByInterfaceScene(string sceneName)
    {
        if (PreLoad(sceneName) < 1)
        {
            IsLoaded = true;
        }
        else
        {
            IsLoaded = false; queen.Load();
            UpdateMgr.GetSingleton().AddUpdate(queen);
        }
    }

    /// <summary>
    /// 主场景预加载
    /// </summary>
    /// <param name="sceneName"></param>
    public void LoadResByMainScene(string sceneName)
    {
        IsLoaded = false; PreLoad(sceneName);
        List<string> assetsList = new List<string>();
        for (int i = 0; i < assetsList.Count; i++)
        {
            string resPath = PathUtil.FilePathToABName(assetsList[i]);
            if (!assetDic.ContainsKey(resPath))
            {
                AddLoad(resPath, "");
            }
        }
        queen.Load(); UpdateMgr.GetSingleton().AddUpdate(queen);
    }

    /// <summary>
    /// 加载游戏场景使用资源(收集资源)
    /// </summary>
    public void LoadResByGameScene(string sceneName)
    {
        IsLoaded = false; PreLoad(sceneName);
        List<string> assetsList = new List<string>();
        for (int i = 0; i < assetsList.Count; i++)
        {
            string resPath = PathUtil.FilePathToABName(assetsList[i]);
            if (!assetDic.ContainsKey(resPath))
            {
                AddLoad(resPath, "");
            }
        }
        queen.Load(); UpdateMgr.GetSingleton().AddUpdate(queen);
    }

    /// <summary>
    /// 场景界面预加载
    /// </summary>
    /// <param name="sceneName">场景名</param>
    private int PreLoad(string sceneName)
    {
        List<string> preloadList = new List<string>();
        for (int i = 0; i < preloadList.Count; i++)
        {
            string resPath = PathUtil.FilePathToABName(preloadList[i]);
            if (!assetDic.ContainsKey(resPath))
            {
                AddLoad(resPath);
            }
        }
        return preloadList.Count;
    }

    /// <summary>
    /// 协程加载 可实现同步加载
    /// </summary>
    /// <param name="resPath"></param>
    /// <param name="arg"></param>
    public IEnumerator CoroutineLoad(string resPath, OnLoadArg arg)
    {
        if (arg == null)
        {
            EDebug.LogError("传入的arg参数无效");
            yield break;
        }

        resPath = resPath.Replace("/", "_").Replace(" ", "-");
        if (assetDic.ContainsKey(resPath))
        {
            arg.asset = assetDic[resPath];
            arg.key = resPath;
            arg.loadOk = true;
            yield break;
        }

        if (Utility.GetPlatForm() == EPlatform.Editor && !ConfigAssetMgr.GetSingleton().HasAsset(resPath))
        {
            EDebug.LogError(string.Format("资源文件夹找不到该资源：{0}可能是大小写不一致问题,请查证", resPath));
            yield break;
        }

        string resUrl = PathUtil.CheckPath(resPath);
        string prePath = null; string[] depends; ABData abData;
        if (ConfigAssetMgr.GetSingleton().GetDepends(resPath, out depends))
        {
            for (int i = 0; i < depends.Length; i++)
            {
                string url = PathUtil.CheckPath(depends[i]); 
                if (!dependCacheDic.TryGetValue(depends[i], out abData))
                {
                    abData = new ABData();
                    dependCacheDic.Add(depends[i], abData);
                    //开始加载资源
                    queen.Add(new WWWLoader(url, ConstData.DEPEND_KEY, depends[i]));
                    //添加前置资源
                    abData.prePath = prePath;
                }
                prePath = depends[i]; abData.refNum++;
            }
        }

        ABData targetABData;
        if (!dependCacheDic.TryGetValue(resPath, out targetABData))
        {
            targetABData = new ABData();
            //增加前置资源
            targetABData.prePath = prePath;
            dependCacheDic.Add(resPath, targetABData);
            queen.Add(new WWWLoader(resUrl, resPath, resPath));
            UpdateMgr.GetSingleton().AddUpdate(queen);
            queen.Load();
        }
        targetABData.refNum++; bool isFind = false;
        do
        {
            if (assetDic.ContainsKey(resPath))
            {
                isFind = true;
                arg.asset = assetDic[resPath];
                arg.loadOk = true;
                arg.key = resPath;
            }
            yield return null;
        }
        while (!isFind); yield break;
    }

    /// <summary>
    /// 加载资源
    /// </summary>
    /// <param name="resPath">资源路径</param>
    /// <param name="loadedHandler">资源加载完成回调函数</param>
    /// <param name="key">加载完成时回调参数</param>
    public void LoadRes(string resPath, OnLoadAsset loadedHandler, string key = "")
    {
        resPath = resPath.Replace("/", "_").Replace(" ", "-");
        if (assetDic.ContainsKey(resPath))
        {
            //已加载资源
            if (loadedHandler != null)
            {
                loadedHandler.Invoke(new OnLoadArg(key, assetDic[resPath], true));
            }
            return;
        }
        AddLoad(resPath, key, loadedHandler); queen.Load();
        UpdateMgr.GetSingleton().AddUpdate(queen);
    }

    //创建Loader(自身和依赖)，加到队列
    private void AddLoad(string resPath, string key = "", OnLoadAsset loadedHandler = null)
    {
        if (Utility.GetPlatForm() == EPlatform.Editor && !ConfigAssetMgr.GetSingleton().HasAsset(resPath))
        {
            EDebug.LogError(string.Format("资源文件夹找不到该资源：{0}可能是大小写不一致问题,请查证", resPath));
            return;
        }

        string prePath = null; string[] depends; ABData abData;
        if (ConfigAssetMgr.GetSingleton().GetDepends(resPath, out depends))
        {
            for (int i = 0; i < depends.Length; i++)
            {
                if (!dependCacheDic.TryGetValue(depends[i], out abData))
                {
                    abData = new ABData();
                    dependCacheDic.Add(depends[i], abData);
                    Load(PathUtil.CheckPath(depends[i]), ConstData.DEPEND_KEY, depends[i], null);
                    //添加前置资源
                    abData.prePath = prePath;
                }
                prePath = depends[i]; abData.refNum++;
            }
        }

        ABData targetABData;
        if (!dependCacheDic.TryGetValue(resPath, out targetABData))
        {
            targetABData = new ABData();
            targetABData.prePath = prePath;
            dependCacheDic.Add(resPath, targetABData);
            Load(PathUtil.CheckPath(resPath), key, resPath, loadedHandler);
        }
        else if (loadedHandler != null)
        {
            if (onloadDic.ContainsKey(resPath))
            {
                onloadDic[resPath] += loadedHandler;
            }
            else
            {
                onloadDic.Add(resPath, loadedHandler);
            }
        }
        targetABData.refNum++;
    }

    //创建Loader(自身和依赖)，加到队列
    private void Load(string url, string key, string resPath, OnLoadAsset loadedHandler)
    {
        if (string.IsNullOrEmpty(key)) { key = url; }

        if (onloadDic.ContainsKey(resPath))
        {
            //资源正在加载中
            onloadDic[resPath] += loadedHandler;
        }
        else
        {
            //开始加载资源
            queen.Add(new WWWLoader(url, key, resPath));
            if (loadedHandler != null)
            {
                onloadDic.Add(resPath, loadedHandler);
            }
        }
    }

    /// <summary>
    /// 加载资源
    /// </summary>
    /// <param name="resPath">资源路径</param>
    /// <param name="TypeFilter">资源类型</param>
    public Object LoadResource(string resPath, Type TypeFilter = null)
    {
#if NO_UAB
        return Resources.Load(resPath);
#endif
        string suffix = ".prefab.uab";
        if (TypeFilter != null && suffixDic.ContainsKey(TypeFilter))
        {
            suffix = suffixDic[TypeFilter];
        }
        resPath = resPath.Replace("/", "_").Replace(" ", "-") + suffix;
        
        if (Utility.GetPlatForm() == EPlatform.Editor && !ConfigAssetMgr.GetSingleton().HasAsset(resPath))
        {
            EDebug.Log(string.Format("资源文件夹找不到该资源：{0}可能是大小写不一致问题,请查证", resPath));
            return null;
        }

        if (!assetDic.ContainsKey(resPath))
        {
            EDebug.Log(string.Format("获取资源出错,请核实{0}资源是否加载", resPath));
        }
        else
        {
            FixShader(assetDic[resPath] as GameObject);
            return assetDic[resPath];
        }
        return null;
    }

    /// <summary>
    /// 加载固定资源  不做更新的
    /// </summary>
    /// <param name="resPath">资源路径</param>
    /// <param name="typeFilter">资源类型</param>
    public Object LoadFixedRes(string resPath, Type typeFilter = null)
    {
        Object obj = null;
        if (typeFilter == null)
        {
            obj = Resources.Load(resPath);
        }
        else
        {
            obj = Resources.Load(resPath, typeFilter);
        }
        if (obj == null)
        {
            EDebug.Log(string.Format("加载资源出错,请核实{0}资源是否存在", resPath));
        }
        return obj;
    }

    public bool IsABLoaded(string resPath, ref string depend)
    {
        string[] depends; ABData abData;
        if (ConfigAssetMgr.GetSingleton().GetDepends(resPath, out depends))
        {
            for (int i = 0; i < depends.Length; i++)
            {
                if (!dependCacheDic.TryGetValue(depends[i], out abData))
                {
                    if (abData.Status != ABDataStatus.abloaded)
                    {
                        depend = depends[i];
                        return true;
                    }
                }
            }
        }
        return false;
    }

    /// <summary>
    /// 设置物体的材质
    /// </summary>
    /// <param name="go"></param>
    public static void FixShader(GameObject go)
    {
        if (go == null) { return; }
        Shader shader; Shader shader2; Material[] materials;
        Renderer[] components = go.GetComponentsInChildren<Renderer>(true);

        for (int i = 0; i < components.Length; i++)
        {
            materials = components[i].sharedMaterials;
            for (int k = 0; k < materials.Length; k++)
            {
                if (materials[k] == null) { continue; }
                shader = materials[k].shader;
                if (shader == null) { continue; }
                shader2 = Shader.Find(shader.name);
                if (shader2 == null) { continue; }
                materials[k].shader = shader2;
            }
        }
    }
    
    /// <summary>
    /// 加载图集并且设置图片
    /// </summary>
    /// <param name="sp"></param>
    /// <param name="atlasName"></param>
    /// <param name="spName"></param>
    public IEnumerator LoadAtlasSetSp(UISprite sp, string atlasName, string spName)
    {
        OnLoadArg arg = new OnLoadArg();
        string fileName = "Atlas/" + atlasName + ".prefab.uab";
        yield return BehaviourUtil.StartCoroutine(CoroutineLoad(fileName, arg));

        if (!arg.loadOk)
        {
            EDebug.LogError("加载不到图集" + atlasName);
            yield break;
        }

        GameObject obj = (arg.asset as GameObject);
        UIAtlas atlas = obj.GetComponent<UIAtlas>();
        sp.atlas = atlas; sp.spriteName = spName;
    }
}